
case class Rank(rank: Double)
object Iteration
object OneIterationDone
object Stop

class GraphNode extends LoggedActor ("pagerank") {
    var inNeighbor: List[GraphNode] = List()
    var outNeighbor: List[GraphNode] = List()
    var inDegree = 0
    var outDegree = 0

    var currentRank = 1.0.toDouble/example.N
    val d = 0.85.toDouble
    var const = (1-d)/example.N

    val LOG = new ActorLog("pagerank", Const.getTime(), this)

    def act() {
        var sigma = 0.0.toDouble
        var inMsg = 0
        loop{
            react{
                case Rank(rank) =>
                    sigma += rank
                    inMsg += 1
                    if (inMsg == inNeighbor.size){
                        currentRank = const + d*sigma
                        LOG.msg_out(OneIterationDone, example.toString)
                        example ! OneIterationDone
                    }
                case Iteration=>
                    sigma = 0
                    for(nb <- outNeighbor){
                        val newrank = Rank(currentRank/outDegree)
                        LOG.msg_out(newrank, example.toString)
                        nb ! newrank
                    }
                case Stop => exit
            }
        }
    }

    def addInNeighbor(in: GraphNode){
        inNeighbor = in::inNeighbor
        inDegree += 1
    }

    def addOutNeighbor(out: GraphNode){
        outNeighbor = out::outNeighbor
        outDegree += 1
    }
}

object example extends LoggedActor ("pagerank") {
    var N = 4
    var maxItr = 1
    val nodes: Array[GraphNode] = new Array[GraphNode](N)

    val LOG = new ActorLog("pagerank", Const.getTime(), this)

    for (i <- 0 until N){
        nodes(i) = new GraphNode()
    }

    var itr = 0
    def act() {
        var finishedNode = 0
        for (node <- nodes){
            node.start
        }
        for (node <- nodes){
            LOG.msg_out(Iteration, node.toString)
            node ! Iteration
        }
        loop{
            react{
                case OneIterationDone =>
                    finishedNode += 1
                    if (finishedNode == N){
                        itr += 1
                        for (node <- nodes){
                            println(node.currentRank)
                        }
                        if (itr == maxItr){
                            for (node <- nodes){
                                LOG.msg_out(Stop, node.toString)
                                node ! Stop
                            }
                            exit
                        } else {
                            for (node <- nodes){
                                LOG.msg_out(Iteration, node.toString)
                                node ! Iteration
                            }
                        }
                    }
            }
        }
    }

    def main(args: Array[String]) {
        nodes(1).addOutNeighbor(nodes(0))
        nodes(1).addOutNeighbor(nodes(2))
        nodes(2).addOutNeighbor(nodes(0))
        nodes(3).addOutNeighbor(nodes(0))
        nodes(3).addOutNeighbor(nodes(1))
        nodes(3).addOutNeighbor(nodes(2))

        nodes(0).addInNeighbor(nodes(1))
        nodes(0).addInNeighbor(nodes(2))
        nodes(0).addInNeighbor(nodes(3))
        nodes(1).addInNeighbor(nodes(3))
        nodes(2).addInNeighbor(nodes(1))
        nodes(2).addInNeighbor(nodes(3))
        start
    }
}